
#include <assert.h>
#include "common.h"
#include "timer.h"

namespace datashuttle
{

TimerTable::TimerTable()
	:mTable(0),mExtraRow(0), mFirstRowIndex(0),mbRunning(false),mDueTimeOfFirstRow(0),mTaskCount(0)
{
}

TimerTable::~TimerTable()
{
	clear();
}

void TimerTable::start( size_t rowcount, size_t itemsperrow )
{
	++rowcount;
	rowcount = std::max( rowcount, (size_t)2 );
	itemsperrow = std::max( itemsperrow, (size_t)2 );

	mTaskTable.resize( rowcount );
	mTable = &mTaskTable[0];

	for( size_t i = 0 ; i < mTaskTable.size() ; i ++ )
	{
		TableRow* row = new TableRow();
		row->init( itemsperrow );					
		mTable[i] = row;
	}
	mExtraRow = new TableRow();
	mExtraRow->init( itemsperrow );

	mFirstRowIndex =  0;
	mbRunning = true;
}

TableRow* TimerTable::firstRow()
{
	return mTable[mFirstRowIndex];
}

bool TimerTable::hasTasks() 
{
	return mTaskCount > 0;
}

TableRow* TimerTable::getRow( size_t index )
{
	size_t idx = (index + mFirstRowIndex) % mTaskTable.size();
	return mTable[idx];
}

void TimerTable::stop()
{
	mbRunning = false;
}

void TimerTable::clear( )
{
	TaskTable::iterator it = mTaskTable.begin();
	for( ; it != mTaskTable.end() ; it ++ )
	{
		TableRow* row = *it;
		if(!row)
			continue;
		row->cancelAllTasks();
		delete row;
	}
	mTaskTable.clear();
	mTable = 0;	
	if(mExtraRow)
	{
		mExtraRow->cancelAllTasks();
		delete mExtraRow;
		mExtraRow = 0;
	}
}

void TimerTable::removeTask( size_t rowId, size_t colId )
{
	if( rowId == (size_t)-1 || colId == (size_t)-1 )
		return;

	if( rowId >= mTaskTable.size() )
	{
		mExtraRow->removeTask(colId);
	}
	else
	{
		mTable[rowId]->removeTask(colId);
	}
}

size_t TimerTable::addTask( BaseTaskPtr task, long long tte, size_t* colId )
{
	if( !task || !mbRunning )
		return false;//null task is not permitted
	// timertable is a almost fixed size multi-dimension table
	if( tte < 0 )
		tte = 0;//negative expiration is not permitted

	if( mDueTimeOfFirstRow <= 0 )
	{//update row 0's expiration time if it's not set
		long long now = timenow();
		mDueTimeOfFirstRow = now;
	}

	TableRow* row = NULL;
	if( tte >= (long long)mTaskTable.size() )
	{// timer table can't hold this task 
		row = mExtraRow;
	}
	else
	{
		row = getRow(tte);
	}
	assert( row != NULL );
	size_t tmpColId =  row->addTask(task);
	if(colId)*colId = tmpColId;

	return row->tableRowId;
}

void TimerTable::adjustExtraRow()
{
	size_t itemcount = mExtraRow->taskcount();
	long long now = timenow();
	for( size_t i = 0 ; i < itemcount ; i ++)
	{
		BaseTaskPtr p = mExtraRow->nextTask();
		if(!p)
			return;
		addTask( p , p->targetTime() - now );
	}
}

size_t TimerTable::extraItemCount() const
{
	return mExtraRow->taskcount();
}
BaseTaskPtr TimerTable::getTask( long long& timetosleep)
{
	// remember to update mDueTimeOfFirstRow if no more tasks in current row
	// 如果一行中含有不是改时间点到期的task，那么需要将该task向后调整
	TableRow* row = firstRow();
	if(row->taskcount() <= 0 )
	{
		mFirstRowIndex++;
		mFirstRowIndex = mFirstRowIndex % mTaskTable.size();

		if( mDueTimeOfFirstRow <= 0 )
			mDueTimeOfFirstRow = timenow();
		else
			mDueTimeOfFirstRow++;
		adjustExtraRow(); //TODO: 我不想太频繁的调用adjustExtraRow,希望借此能够减少一些cpu消耗
		return NULL;
	}
	
	BaseTaskPtr p = row->nextTask();

	if( row->taskcount() <= 0 )
	{
		mDueTimeOfFirstRow ++;

		mFirstRowIndex++;
		mFirstRowIndex = mFirstRowIndex % mTaskTable.size();
		adjustExtraRow();//TODO: 同样的，希望可以减少对adjustExtraRow的调用
	}
	

	return p;
}


// table的一行包含该时间点将到期的所有的task

size_t TableRow::taskcount() const
{
	if( lastIndex == firstIndex )
	{
		return 0;
	}
	else if( lastIndex > firstIndex )
	{
		return lastIndex - firstIndex;
	}
	else
	{
		return lastIndex + tasklst.size() - firstIndex;
	}
}

void TableRow::init( size_t itemcount )
{
	itemcount = std::max(itemcount,(size_t)2);
	tasklst.resize( itemcount );
	tasks = &tasklst[0];
	firstIndex = lastIndex = 0; // this means that there is no tasks in current table row
}

void TableRow::expand( )
{
	size_t oldsize = tasklst.size();
	tasklst.resize( tasklst.size() * 2 );
	tasks = &tasklst[0];

	if( lastIndex < firstIndex )
	{
		for( size_t i = 0 ; i < lastIndex ; i++ ,oldsize ++)
		{
			tasks[oldsize] = tasks[i];
		}
	}
	lastIndex = oldsize;
	//TODO: add a log message or something else
	//      so that we know this row expand its size 
	//      to hold more task item
	
}

size_t TableRow::addTask( BaseTaskPtr task )
{
	if(!task)	
		return (size_t)-1;//no null task is accepted

	if( lastIndex >= tasklst.size() )
	{
		assert(false);//should never be here
	}
	//if the row is full with tasks, we have to expand the row's capacity for more tasks
	if( (lastIndex + 1 == firstIndex) || (  lastIndex == 0 && firstIndex == ( tasklst.size() -1 ) ) )
	{
		expand();
	}
	size_t colId = lastIndex;
	tasks[lastIndex] = task;
	lastIndex ++;
	if ( lastIndex >= tasklst.size() )
	{
		if( firstIndex <= 0 )
		{//no more room for new tasks
			expand();
		}
		else
		{
			lastIndex = 0;
		}
	}
	return colId;
}

void TableRow::removeTask( size_t colId )
{
	if( colId >= tasklst.size() )
		return;
	tasklst[colId] = 0;
}

void TableRow::cancelAllTasks()
{
	size_t count = taskcount();
	for( size_t i = 0 ; i < count ; i ++)
	{
		BaseTaskPtr p = nextTask();
		if(p)
		{
			p->cancel();
		}
	}
}

BaseTaskPtr TableRow::nextTask()
{
	if(lastIndex == firstIndex)
		return NULL;//no task in current table row

	BaseTaskPtr p = tasks[firstIndex];
	firstIndex++;
	if( firstIndex >= tasklst.size() )
		firstIndex = 0;
	return p;
}

}//namespace datashuttle


